<?php
declare(strict_types=1);

namespace app\service;

use app\model\{MerchantPaymentConfig, UserPaymentRecord};
use think\Exception;

class PaymentService
{
    protected $merchant;
    protected $config;

    public function __construct($merchant = null)
    {
        $this->merchant = $merchant;
        if ($merchant) {
            $this->config = MerchantPaymentConfig::where([
                'merchant_id' => $merchant->id,
                'status' => 1
            ])->column('payment_config', 'payment_method');
        }
    }

    /**
     * 创建支付参数
     * @param UserPaymentRecord $payment 支付记录
     * @param string $method 支付方式
     * @return array
     * @throws Exception
     */
    public function createPayment(UserPaymentRecord $payment, string $method): array
    {
        if (!isset($this->config[$method])) {
            throw new Exception('支付方式未配置');
        }

        $config = json_decode($this->config[$method], true);
        
        switch ($method) {
            case 'wechat':
                return $this->createWechatPayment($payment, $config);
            case 'alipay':
                return $this->createAlipayPayment($payment, $config);
            default:
                throw new Exception('不支持的支付方式');
        }
    }

    /**
     * 创建微信支付参数
     */
    protected function createWechatPayment(UserPaymentRecord $payment, array $config): array
    {
        // 配置参数
        $params = [
            'appid' => $config['app_id'],
            'mch_id' => $config['mch_id'],
            'nonce_str' => $this->generateNonceStr(),
            'body' => '快递费用',
            'out_trade_no' => $payment->id,
            'total_fee' => intval($payment->payment_amount * 100), // 转为分
            'spbill_create_ip' => request()->ip(),
            'notify_url' => $config['notify_url'],
            'trade_type' => 'JSAPI', // 或MWEB,APP等
            'openid' => $payment->user->openid // 需要在User模型中关联
        ];

        // 签名
        $params['sign'] = $this->generateWechatSign($params, $config['key']);

        // 调用微信统一下单接口
        $result = $this->postWechatApi('https://api.mch.weixin.qq.com/pay/unifiedorder', $params);
        
        if ($result['return_code'] != 'SUCCESS' || $result['result_code'] != 'SUCCESS') {
            throw new Exception($result['return_msg'] ?? '微信支付下单失败');
        }

        // 生成支付参数
        $payParams = [
            'appId' => $config['app_id'],
            'timeStamp' => (string)time(),
            'nonceStr' => $this->generateNonceStr(),
            'package' => 'prepay_id=' . $result['prepay_id'],
            'signType' => 'MD5'
        ];
        $payParams['paySign'] = $this->generateWechatSign($payParams, $config['key']);

        return $payParams;
    }

    /**
     * 创建支付宝支付参数
     */
    protected function createAlipayPayment(UserPaymentRecord $payment, array $config): array
    {
        // 配置参数
        $params = [
            'app_id' => $config['app_id'],
            'method' => 'alipay.trade.create',
            'format' => 'JSON',
            'charset' => 'utf-8',
            'sign_type' => 'RSA2',
            'timestamp' => date('Y-m-d H:i:s'),
            'version' => '1.0',
            'notify_url' => $config['notify_url'],
            'biz_content' => json_encode([
                'out_trade_no' => $payment->id,
                'total_amount' => $payment->payment_amount,
                'subject' => '快递费用',
                'buyer_id' => $payment->user->alipay_user_id // 需要在User模型中关联
            ])
        ];

        // 签名
        $params['sign'] = $this->generateAlipaySign($params, $config['private_key']);

        return $params;
    }

    /**
     * 验证支付回调
     */
    public function verifyNotify(array $params): ?array
    {
        // 根据回调参数中的商户订单号查询支付记录
        $payment = UserPaymentRecord::where('id', $params['out_trade_no'])->find();
        if (!$payment) {
            return null;
        }

        // 获取支付配置
        $config = MerchantPaymentConfig::where([
            'merchant_id' => $payment->merchant_id,
            'payment_method' => $payment->payment_method,
            'status' => 1
        ])->value('payment_config');

        if (!$config) {
            return null;
        }

        $config = json_decode($config, true);

        // 根据支付方式验证签名
        $verified = false;
        if ($payment->payment_method == 'wechat') {
            $verified = $this->verifyWechatSign($params, $config['key']);
        } elseif ($payment->payment_method == 'alipay') {
            $verified = $this->verifyAlipaySign($params, $config['public_key']);
        }

        return $verified ? [
            'order_no' => $payment->order_no,
            'payment_amount' => $payment->payment_amount
        ] : null;
    }

    // 工具方法...
    protected function generateNonceStr($length = 32): string
    {
        $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
        $str = '';
        for ($i = 0; $i < $length; $i++) {
            $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
        }
        return $str;
    }

    protected function generateWechatSign(array $params, string $key): string
    {
        ksort($params);
        $string = '';
        foreach ($params as $k => $v) {
            if ($v !== '' && $v !== null && $k != 'sign') {
                $string .= $k . '=' . $v . '&';
            }
        }
        $string .= 'key=' . $key;
        return strtoupper(md5($string));
    }

    protected function generateAlipaySign(array $params, string $privateKey): string
    {
        ksort($params);
        $string = '';
        foreach ($params as $k => $v) {
            if ($v !== '' && $v !== null && $k != 'sign') {
                $string .= $k . '=' . $v . '&';
            }
        }
        $string = rtrim($string, '&');
        
        $key = "-----BEGIN RSA PRIVATE KEY-----\n" .
            wordwrap($privateKey, 64, "\n", true) .
            "\n-----END RSA PRIVATE KEY-----";
            
        openssl_sign($string, $sign, $key, OPENSSL_ALGO_SHA256);
        return base64_encode($sign);
    }

    protected function verifyWechatSign(array $params, string $key): bool
    {
        $sign = $params['sign'];
        unset($params['sign']);
        return $sign === $this->generateWechatSign($params, $key);
    }

    protected function verifyAlipaySign(array $params, string $publicKey): bool
    {
        $sign = base64_decode($params['sign']);
        unset($params['sign'], $params['sign_type']);
        
        ksort($params);
        $string = '';
        foreach ($params as $k => $v) {
            $string .= $k . '=' . $v . '&';
        }
        $string = rtrim($string, '&');
        
        $key = "-----BEGIN PUBLIC KEY-----\n" .
            wordwrap($publicKey, 64, "\n", true) .
            "\n-----END PUBLIC KEY-----";
            
        return openssl_verify($string, $sign, $key, OPENSSL_ALGO_SHA256) === 1;
    }

    protected function postWechatApi(string $url, array $data): array
    {
        $xml = '<xml>';
        foreach ($data as $key => $val) {
            $xml .= '<' . $key . '>' . $val . '</' . $key . '>';
        }
        $xml .= '</xml>';

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
        $response = curl_exec($ch);
        curl_close($ch);

        libxml_disable_entity_loader(true);
        return json_decode(json_encode(simplexml_load_string($response, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
    }
} 